

import { Logger } from "../utils/Logger";
import { SocketClientReqManager } from "./SocketClientReqManager";
import SocketRequestData from "./SocketRequestData";
import { WebSocketClientManager } from "./WebSocketClientManager";
import { ProtoManager, WsSendData } from "../../lib/net/protobuf/ProtoManager";
import { JhaoProto } from "../../lib/net/protobuf/jhaoproto";
import DateUtil from "../utils/DateUtil";

const ws = require("ws");


export class WebSocketClient {

    private ws;
    private isConnecting: boolean;
    private isConnected: boolean;
    private requestMsgQue: Array<SocketRequestData> = [];

    private reqId: number = 1;
    public id: number;
    public url: string;

    private static count: number = 0
    private requestMsgMap: Map<number, SocketRequestData> = new Map<number, SocketRequestData>();
    private isDestroy: boolean;
    public account: string;

    public init(url: string, id: number) {
        try {
            if (!this.isConnected && !this.isConnecting) {
                this.isConnecting = true
                this.url = url;
                this.id = id;
                this.ws = new ws(url);
                Logger.log("WebSocketClient===init=", url, this.id)
                this.ws.on('open', () => {
                    WebSocketClient.count++;
                    this.isConnecting = false;
                    Logger.info("ws onConnect===", this.url, this.id, WebSocketClient.count);
                    this.isConnected = true;
                    this.checkSendPack();
                });
                this.ws.on('message', (socketData) => {
                    this.onGameMsg(socketData)
                })
                this.ws.on("error", (err) => {
                    this.isConnected = false;
                    this.isConnecting = false;
                    Logger.error("WebSocketClient error=====", this.id, err)
                });
                this.ws.on('close', () => {
                    this.isConnected = false;
                    this.isConnecting = false;
                    Logger.info('disconnected', this.id, this.url);
                    this.destroy();
                });
            }
        } catch (error) {
            Logger.error("WebSocketClient error=", error)
        }
    }

    private onGameMsg(socketData) {
        try {
            let byteArray: Uint8Array = new Uint8Array(socketData);
            let baseMsg: JhaoProto.BaseMsg = ProtoManager.instance.decode(JhaoProto.BaseMsg, byteArray)
            let socketReqData: SocketRequestData | undefined = this.requestMsgMap.get(baseMsg.cmdOrder);
            if (socketReqData) {
                this.requestMsgMap.delete(baseMsg.cmdOrder)
                if (socketReqData.data.callback) {
                    socketReqData.data.callback(baseMsg)
                } else {
                    SocketClientReqManager.instance.onSocketMsg(this, baseMsg)
                }
            }else{
                SocketClientReqManager.instance.onSocketMsg(this, baseMsg)
            }

        } catch (error) {
            Logger.error("onGameMsg======error==", error);
            this.destroy();
        }
    }

    public async sendDataAsync(protoPack): Promise<any> {
        return new Promise((resolve, reject) => {
            this.sendData(protoPack, (data: object) => {
                resolve(data)
            })
        })
    }

    public sendData(protoPack, callback: Function=null) {
        let reqData: SocketRequestData = new SocketRequestData();
        reqData.id = this.reqId;
        this.reqId++;
        reqData.data = protoPack;
        reqData.callback = callback;
        this.requestMsgQue.push(reqData);
        if (this.isConnected) {
            this.checkSendPack();
        } else {
            this.init(this.url, this.id);
        }
    }

    private checkOnlyId() {
        let abc: number = this.reqId;
        this.reqId++;
        if (this.reqId >= Number.MAX_SAFE_INTEGER) {
            this.reqId = 1;
        }
        return abc;
    }

    public sendProto(sendData: WsSendData) {
        let reqData: SocketRequestData = new SocketRequestData();
        reqData.id = this.checkOnlyId();
        reqData.data = sendData;
        this.requestMsgQue.push(reqData);
        Logger.log("sendProto==", sendData, this.requestMsgQue)
        if (this.isConnected) {
            this.checkSendPack();
        } else {
            if (!this.isConnecting) {
                this.init(this.url, this.id);
            }
        }
    }

    private checkSendPack() {
        if (this.requestMsgQue.length > 0) {
            if (this.isConnected) {
                let socketData: SocketRequestData = this.requestMsgQue.shift();
                this.send(this.createSendPack(socketData));
                this.checkSendPack();
            }else{
                if (!this.isConnecting) {
                    this.init(this.url, this.id);
                }
            }
        }
    }

    /**
 * 创建发送包
 * @param protoPack 
 */
    private createSendPack(socketData: SocketRequestData) {
        // let data: object = {
        //     "id": socketData.id,
        //     "data": socketData.data
        // }
        // this.respCheckMap.set(socketData.id, socketData)
        // return JSON.stringify(data)
        let send_data: JhaoProto.IBaseMsg = {
            cmdOrder: socketData.id,
            cmdIndex: socketData.data.cmd_index,
            timeUpload: DateUtil.now(),
            data: ProtoManager.instance.encode(socketData.data.en_proto_ctor, socketData.data.data)
        }
        this.requestMsgMap.set(socketData.id, socketData)
        return ProtoManager.instance.encode(JhaoProto.BaseMsg, send_data)
    }


    private send(data) {
        if (this.isConnected) {
            this.ws.send(data);
        }
    }

    public close() {
        if (this.isConnected) {
            Logger.log("WebSocketClient.close")
            this.ws.close();
        }
    }

    public destroy() {
        if (!this.isDestroy) {
            this.isConnecting = false;
            this.isConnected = false
            this.isDestroy = true;
            WebSocketClientManager.instance.destoyWebSocketClient(this)
            this.account = null
            this.ws.removeAllListeners()
            this.ws = null;
            this.requestMsgMap.clear();
            this.requestMsgMap = null;
            this.requestMsgQue = null;
        }
    }
}